﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml;
using System.Xml.Serialization;

namespace LogParser
{
    public class AdBaseRequestParser
    {
        public static bool GetBingRequestBody(string strInBody, out string strBody)
        {
            bool valid = true;
            strBody = null;

            BingRequest bingRequest;
            valid = TryParse(strInBody, out bingRequest);
            if (valid)
            {
                valid = TryResponseFromBingReponse(bingRequest, out strBody);
            }
            else
            {
                return false;
            }
            return valid;
        }

        private static bool TryResponseFromBingReponse(BingRequest parsedRequest, out string strBody)
        {
            XmlSerializer xml = new XmlSerializer(typeof(BingRequest));
            try
            {
                using (MemoryStream Stream = new MemoryStream())
                using (StreamWriter writer = new StreamWriter(Stream, Encoding.UTF8))
                {
                    XmlWriterSettings xmlWriterSettings = new XmlWriterSettings();
                    xmlWriterSettings.Indent = true;
                    XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
                    ns.Add("bdi", "http://schemas.microsoft.com/bing/bdi");

                    using (XmlWriter xmlWriter = XmlWriter.Create(writer, xmlWriterSettings))
                    {
                        xmlWriter.WriteStartDocument(false);
                        xml.Serialize(xmlWriter, parsedRequest, ns);
                    }
                    writer.Flush();
                    Stream.Seek(0, SeekOrigin.Begin);
                    using (StreamReader sr = new StreamReader(Stream))
                    {
                        strBody = sr.ReadToEnd();
                    }
                }
            }
            catch (InvalidOperationException)
            {
                strBody = null;
                return false;
            }
            return true;
        }

        private static bool TryParse(string strBody, out BingRequest parsedRequest)
        {
            bool valid = true;
            parsedRequest = new BingRequest();
            try
            {
                using (MemoryStream stream = new MemoryStream(Encoding.Default.GetBytes(strBody)))
                using (XmlReader xmlReader = XmlReader.Create(stream))
                {
                    while (valid && !xmlReader.EOF)
                    {
                        if (xmlReader.NodeType == XmlNodeType.Element)
                        {
                            switch (xmlReader.LocalName)
                            {
                                case "AdBaseRequest":
                                    valid = ParseBingRequest(xmlReader.ReadSubtree(), parsedRequest);
                                    break;
                                default:
                                    xmlReader.Read();
                                    break;
                            }
                        }
                        else
                        {
                            xmlReader.Read();
                        }
                    }
                }
            }
            catch (Exception)
            {
                valid = false;
                parsedRequest = null;
            }
            return valid;
        }

        private static bool ParseBingRequest(XmlReader xmlReader, BingRequest parsedRequest)
        {
            bool valid = true;
            while (valid && !xmlReader.EOF)
            {
                if (xmlReader.NodeType == XmlNodeType.Element)
                {

                    switch (xmlReader.LocalName)
                    {
                        case "Query":
                            parsedRequest.Query = xmlReader.ReadElementContentAsString();
                            break;
                        case "QueryType":
                            parsedRequest.QueryType = (uint)xmlReader.ReadElementContentAsInt();
                            break;
                        case "QueryHint":
                            parsedRequest.QueryHint = xmlReader.ReadElementContentAsString();
                            break;
                        case "UserEntryPoint":
                            parsedRequest.UserEntryPoint = xmlReader.ReadElementContentAsString();
                            break;                             
                        case "Ad":
                            valid = ParseAdRequest(xmlReader.ReadSubtree(), parsedRequest);
                            break;
                        default:
                            xmlReader.Read();
                            break;
                    }
                }
                else
                {
                    xmlReader.Read();
                }
            }
            return valid;
        }
        private static bool ParseAdRequest(XmlReader xmlReader, BingRequest parsedRequest)
        {
            bool valid = true;
            while (valid && !xmlReader.EOF)
            {
                if (xmlReader.NodeType == XmlNodeType.Element)
                {
                    if (xmlReader.LocalName == "Ad")
                    {
                        parsedRequest.Requests = new Requests();
                        parsedRequest.Requests.Request = new Request();
                        parsedRequest.Requests.Request.AdRequest = new AdRequest();
                        xmlReader.Read();
                    }
                    else
                    {
                        switch (xmlReader.LocalName)
                        {
                            case "AdUnitId":
                                parsedRequest.Requests.Request.AdRequest.AdUnitId = (uint)xmlReader.ReadElementContentAsInt();
                                break;
                            case "PropertyId":
                                parsedRequest.Requests.Request.AdRequest.PropertyId = (uint)xmlReader.ReadElementContentAsInt();
                                break;
                            case "ChannelId":
                                parsedRequest.Requests.Request.AdRequest.ChannelId = (uint)xmlReader.ReadElementContentAsInt();
                                break;
                            case "DynamicTags":
                                parsedRequest.Requests.Request.AdRequest.DynamicTags = xmlReader.ReadElementContentAsString();
                                break;
                            case "SourceUserProfile":
                                parsedRequest.Requests.Request.AdRequest.SourceUserProfile = xmlReader.ReadElementContentAsString();
                                break;
                            case "DeviceType":
                                parsedRequest.Requests.Request.AdRequest.DeviceType = xmlReader.ReadElementContentAsInt();
                                break;
                            case "TransactionType":
                                parsedRequest.Requests.Request.AdRequest.TransactionType = xmlReader.ReadElementContentAsInt();
                                break;
                            case "MobileNetworkCode":
                                parsedRequest.Requests.Request.AdRequest.MobileNetworkCode = xmlReader.ReadElementContentAsInt();
                                break;
                            case "MobileCountryCode":
                                parsedRequest.Requests.Request.AdRequest.MobileCountryCode = xmlReader.ReadElementContentAsInt();
                                break;
                            case "MlCount":
                                parsedRequest.Requests.Request.AdRequest.MlCount = xmlReader.ReadElementContentAsInt();
                                break;
                            case "SbCount":
                                parsedRequest.Requests.Request.AdRequest.SbCount = xmlReader.ReadElementContentAsInt();
                                break;
                            case "PageNumber":
                                parsedRequest.Requests.Request.AdRequest.PageNumber = (uint)xmlReader.ReadElementContentAsInt();
                                break;
                            case "ClientType":
                                parsedRequest.Requests.Request.AdRequest.ClientType = (uint)xmlReader.ReadElementContentAsInt();
                                break;
                            case "EnableRichAds":
                                parsedRequest.Requests.Request.AdRequest.EnableRichAds = (bool)xmlReader.ReadElementContentAsBoolean();
                                break;
                            case "ExpectedRichAdTypes":
                                parsedRequest.Requests.Request.AdRequest.ExpectedRichAdTypes = (uint)xmlReader.ReadElementContentAsInt();
                                break;
                            case "AdLanguage":
                                parsedRequest.Requests.Request.AdRequest.AdLanguage = xmlReader.ReadElementContentAsString();
                                break;
                            case "WebSiteCountry":
                                parsedRequest.Requests.Request.AdRequest.WebSiteCountry = xmlReader.ReadElementContentAsString();
                                break;
                            case "EnableKWDTextDecoration":
                                parsedRequest.Requests.Request.AdRequest.EnableKWDTextDecoration = (uint)xmlReader.ReadElementContentAsInt();
                                break;
                            case "KWDTextDecorationMode":
                                parsedRequest.Requests.Request.AdRequest.KWDTextDecorationMode = xmlReader.ReadElementContentAsString();
                                break;
                            case "SupportedExtensionsMask":
                                parsedRequest.Requests.Request.AdRequest.SupportedExtensionsMask = (uint)xmlReader.ReadElementContentAsInt();
                                break;
                            case "DeviceCapabilities":
                                parsedRequest.Requests.Request.AdRequest.DeviceCapabilities = xmlReader.ReadElementContentAsString();
                                break;
                            default:
                                xmlReader.Read();
                                break;
                        }
                    }
                }
                else
                {
                    xmlReader.Read();
                }
            }
            return valid;
        }

    }
}
